Herein, we present MenpoWidgets
's basic widget tools that implement lower level widget functionalities, such as colour selection, zoom options, axes options, etc. These are the main ingredients in order to synthesize higher-level widget classes, such as the ones presented in Widgets Components.ipynb. All the widgets of this category live in menpowidgets.tools
.
Below we present the functionalities of each one of them separately. Specifically we split this notebook in the following subsections:
As explained in the Introduction.ipynb notebook, all the widgets presented here are subclasses of menpo.abstract.MenpoWidget
, thus they follow the same rules, which are:
add_render_function()
, remove_render_function()
, replace_render_function()
and call_render_function()
.set_widget_state()
, which updates the widget state with a new set of options.style()
which takes a set of options that change the style of the widget, such as font-related options, border-related options, etc.Before presenting each widget separately, let's first import the things that are required.
In [1]:
from menpowidgets.tools import (LogoWidget, ListWidget, SlicingCommandWidget, ColourSelectionWidget,
IndexButtonsWidget, IndexSliderWidget, ZoomOneScaleWidget, ZoomTwoScalesWidget,
ImageOptionsWidget, LineOptionsWidget, MarkerOptionsWidget, NumberingOptionsWidget,
AxesLimitsWidget, AxesTicksWidget, AxesOptionsWidget, LegendOptionsWidget,
GridOptionsWidget, HOGOptionsWidget, DSIFTOptionsWidget, DaisyOptionsWidget,
LBPOptionsWidget, IGOOptionsWidget)
from menpowidgets.style import map_styles_to_hex_colours
Let us also define a generic print function that will be the callback trigger when the selected_values
trait of all the widgets changes.
The function must have a single argument, which will be a dict
with the following keys:
'name'
: The name of the trait that is monitored and triggers the callback. In the case of a MenpoWidget
subclass, this is always 'selected_values'
.'type'
: The type of event that happens on the trait. In the case of a MenpoWidget
subclass, this is always 'change'
.'new'
: The currently selected value attached to selected_values
.'old'
: The previous value of selected_values
.'owner'
: Pointer to the widget object.Consequently, the selected values of a widget object (e.g. wid
) can be retrieved in any of the following 3 equivalent ways:
wid.selected_values
change['new']
change['owner'].selected_values
For this notebook, we choose the second way which is independent of the widget object.
In [2]:
from menpo.visualize import print_dynamic
def render_function(change):
print(change['new'])
This is a simple widget that can be used for embedding an image into an ipywidgets
widget are using the ipywidgets.Image
class.
In [2]:
from menpowidgets.tools import LogoWidget
LogoWidget(style='danger')
MenpoWidgets has a widget for defining a list
of numbers. The widget is smart enough to accept any valid python command, such as
'range(10)', '[1, 2, 3]', '10'
and complain about syntactic mistakes. It can be defined to expect either int
or float
numbers and has an optional example as guide.
In [4]:
list_cmd = [0, 1, 2]
wid = ListWidget(list_cmd, mode='int', description='List:', render_function=render_function, example_visible=True)
wid
Note that you need to press Enter in order to pass a new value into the textbox. Also, try typing a wrong command, such as
'10, 20,,', '10, a, None'
to see the corresponding error messages.
The styling of the widget can be changed using the style()
method.
In [5]:
wid.style(box_style='danger', font_size=15)
The state of the widget can be updated with the set_widget_state()
method. Note that since allow_callback=False
, nothing gets printed after running the command, even though selected_values
is updated.
In [6]:
wid.set_widget_state([20, 16], allow_callback=False)
Similar to the list
widget, MenpoWidgets has a widget for defining a command for slicing a list
(or numpy.array
). Commands can have any vald Python syntax, such as
':3:', '::2', '1:2:10', '-1::', '0, 3, 7', 'range(5)'
The widget gets as argument a dict
with the initial slicing command as well as the length of the list
.
In [7]:
# Initial options
slice_cmd = {'command': ':3',
'length': 10}
# Create widget
wid = SlicingCommandWidget(slice_cmd, description='Command:', render_function=render_function,
example_visible=True, orientation='horizontal')
# Display widget
wid
Note that by defining a single int
number, then an ipywidget.IntSlider
appears that allows to select the index. Similarly, by inserting any slicing command with a constant step, then an ipywidgets.IntRangeSlider
appears. The sliders are disabled when inserting a slicing command with non-constant step. The placement of the sliders with respect to the textbox is controlled by the orientation
argument.
Additionally, similar to the ListWidget
, the widget is smart enough to detect any syntactic errors and print a relevant message.
The styling of the widget can be changed as
In [8]:
wid.style(border_visible=True, border_style='dashed', font_weight='bold')
To update the widget's state, you need to pass in a new dict
of options, as
In [9]:
wid.set_widget_state({'command': ':40', 'length': 40}, allow_callback=True)
MenpoWidgets is using the standard Java colour picker defined in ipywidgets.ColorPicker
. However, ColourSelectionWidget
has the additional functionality to select colours for a set of objects. Thus the widget constructor gets a list
of colours (either the colour name str
or the RGB values), as well as the labels
list
that has the names of the objects.
In [10]:
wid = ColourSelectionWidget([[255, 38, 31], 'blue', 'green'], labels=['a', 'b', 'c'],
render_function=render_function)
# Set styling
wid.style(box_style='warning', apply_to_all_style='info', label_colour='black',
label_background_colour=map_styles_to_hex_colours('info', background=True), font_weight='bold')
# Display widget
wid
The Apply to all button sets the currently selected colour to all the labels.
The colours can also be updated with the set_colours()
function as
In [11]:
wid.set_colours(['red', 'orange', 'pink'], allow_callback=True)
In case there is only one label, defined either with a list
of length 1
or by setting labels=None
, then the drop-down menu to select object does not appear. For example, let's update the state of the widget:
In [12]:
wid.set_widget_state(['red'], None)
The following two widgets give the ability to select a single integer number from a specified range. Thus, they can be seen as index selectors. The user must pass in a dict
that defines the minimum, maximum and step of the allowed range, as well as the initially selected index. Then the selected_values
trait always keeps track of the selected index, thus it has int
type.
An index selection widget, where the selector is an ipywidgets.IntSlider
can be created as
In [13]:
# Initial options
index = {'min': 0,
'max': 100,
'step': 1,
'index': 10}
# Crete widget
wid = IndexSliderWidget(index, description='Index: ', render_function=render_function, continuous_update=False)
# Set styling
wid.style(box_style='danger', slider_handle_colour=map_styles_to_hex_colours('danger'),
slider_bar_colour=map_styles_to_hex_colours('danger'))
# Display widget
wid
As with all widgets, the state can be updated as:
In [14]:
wid.set_widget_state({'min': 10, 'max': 500, 'step': 2, 'index': 50}, allow_callback=True)
An index selection widget where the selection can be performed with -/+ (previous/next) buttons can be created as:
In [15]:
index = {'min': 0, 'max': 100, 'step': 1, 'index': 10}
wid = IndexButtonsWidget(index, render_function=render_function, loop_enabled=False, text_editable=True)
wid
Note that since text_editable
is True
, you can actually edit the index directly from the textbox. Additionally, by setting loop_enabled=True
means that by pressing '+' when the textbox is at the last index, it takes you to the minimum index.
Let's update the styling of the widget:
In [16]:
wid.style(box_style='danger', plus_style='success', minus_style='danger', text_colour='blue',
text_background_colour=map_styles_to_hex_colours('info', background=True))
Let's also update its state with a new set of options:
In [17]:
wid.set_widget_state({'min': 20, 'max': 500, 'step': 2, 'index': 50}, loop_enabled=True, text_editable=True,
allow_callback=True)
There are two widgets for zooming into a figure. Both are using ipywidgets.FloatSLider
and get as input a dict
with the minimum and maximum values, the step of the slider(s) and the initial zoom value.
The first one defines a single zoom float
, as
In [18]:
# Initial options
zoom_options = {'min': 0.1,
'max': 4.,
'step': 0.05,
'zoom': 1.}
# Create widget
wid = ZoomOneScaleWidget(zoom_options, render_function=render_function)
# Set styling
wid.style(box_style='danger')
wid.zoom_slider.background_color = map_styles_to_hex_colours('info')
wid.zoom_slider.slider_color = map_styles_to_hex_colours('danger')
# Display widget
wid
and its state can be updated as:
In [19]:
wid.set_widget_state({'zoom': 0.5, 'min': 0., 'max': 4., 'step': 0.2}, allow_callback=True)
The second one defines two zoom values that are intended to control the height and width of a figure.
In [20]:
# Initial options
zoom_options = {'min': 0.1,
'max': 4.,
'step': 0.1,
'zoom': [1., 1.],
'lock_aspect_ratio': False}
# Create widget
wid = ZoomTwoScalesWidget(zoom_options, render_function=render_function, continuous_update=True)
# Set styling
wid.style(box_style='danger')
# Display widget
wid
Note that the sliders can be linkedd in order to preserve the aspect ratio of the figure. The state can be updated as:
In [21]:
zoom_options = {'min': 0.5, 'max': 10., 'step': 0.3, 'zoom': [2., 3.]}
wid.set_widget_state(zoom_options, allow_callback=True)
This is a widget for selecting options related to rendering an image. It defines the colourmap, the alpha value for transparency as well as the interpolation. Specifically:
In [22]:
# Initial options
image_options = {'alpha': 1.,
'interpolation': 'bilinear',
'cmap_name': None}
# Create widget
wid = ImageOptionsWidget(image_options, render_function=render_function)
# Set styling
wid.style(box_style='success', padding=10, border_visible=True, border_radius=45)
# Display widget
wid
The widget can be updated with a new dict
of options as:
In [23]:
wid.set_widget_state({'alpha': 0.8, 'interpolation': 'none', 'cmap_name': 'gray'}, allow_callback=True)
The following widget allows the selection of options for rendering line objects. The initial options are passed in as a dict
and control the width, style and colour of the lines. Note that a different colour can be defined for different objects using the labels
argument.
In [24]:
# Initial options
line_options = {'render_lines': True,
'line_width': 1,
'line_colour': ['blue', 'red'],
'line_style': '-'}
# Create widget
wid = LineOptionsWidget(line_options, render_function=render_function,
labels=['menpo', 'widgets'])
# Set styling
wid.style(box_style='danger', padding=6)
# Display widget
wid
The Render lines tick box also controls the visibility of the rest of the options. So by updating the state with render_lines=False
, the options disappear.
In [25]:
wid.set_widget_state({'render_lines': False, 'line_width': 5, 'line_colour': ['purple'], 'line_style': '--'},
allow_callback=True, labels=None)
Similar to the LineOptionsWidget
, this widget allows to selecting options for rendering markers. The options define the edge width, face colour, edge colour, style and size of the markers.
In [26]:
# Initial options
marker_options = {'render_markers': True,
'marker_size': 20,
'marker_face_colour': ['red', 'green'],
'marker_edge_colour': ['black', 'blue'],
'marker_style': 'o',
'marker_edge_width': 1}
# Create widget
wid = MarkerOptionsWidget(marker_options, render_function=render_function,
labels=['a', 'b'])
# Set styling
wid.style(box_style='info', padding=6)
# Display widget
wid
In [27]:
wid.set_widget_state({'render_markers': True, 'marker_size': 20, 'marker_face_colour': ['red'],
'marker_edge_colour': ['black'], 'marker_style': 'o', 'marker_edge_width': 1},
labels=None, allow_callback=True)
The NumberingOptionsWidget
is used in case you want to render some numbers next to the plotted points.
In [28]:
# Initial options
numbers_options = {'render_numbering': True,
'numbers_font_name': 'serif',
'numbers_font_size': 10,
'numbers_font_style': 'normal',
'numbers_font_weight': 'normal',
'numbers_font_colour': ['black'],
'numbers_horizontal_align': 'center',
'numbers_vertical_align': 'bottom'}
# Create widget
wid = NumberingOptionsWidget(numbers_options, render_function=render_function)
# Set styling
wid.style(box_style='success', border_visible=True, border_colour='black', border_style='solid', border_width=1,
border_radius=0, padding=10, margin=10)
# Display widget
wid
Of course the state of the widget can be updated as:
In [29]:
wid.set_widget_state({'render_numbering': True, 'numbers_font_name': 'serif', 'numbers_font_size': 10,
'numbers_font_style': 'normal', 'numbers_font_weight': 'normal',
'numbers_font_colour': ['green'], 'numbers_horizontal_align': 'center',
'numbers_vertical_align': 'bottom'}, allow_callback=True)
Before presenting the AxesOptionsWidget
, let's first see two widgets that are ued as its basic components for selecting the axes limits as well as the axes ticks.
AxesLimitsWidget
has 3 basic functions per axis:
auto
: Allows matplotlib
to automatically set the limits.percentage
: It expects a float
that defines the percentage of padding to allow around the rendered object's region.range
: It expects two numbers that define the minimum and maximum values of the limits.
In [30]:
# Create widget
wid = AxesLimitsWidget(axes_x_limits=[0, 10], axes_y_limits=0.1, render_function=render_function)
# Set styling
wid.style(box_style='danger')
# Display widget
wid
Note that the percentage
mode is accompanied by a ListWidget
that expects a single float
, whereas the range
mode invokes a ListWidget
that expects two float
numbers. The state of the widget can be changed as:
In [31]:
wid.set_widget_state([-200, 200], None, allow_callback=True)
On the other hand, AxesTicksWidget
has two functionalities per axis:
auto
: Allows matplotlib
to automatically set the ticks.list
: Enables a ListWidget
to select the ticks.
In [32]:
# Initial options
axes_ticks = {'x': [],
'y': [10., 20., 30.]}
# Create widget
wid = AxesTicksWidget(axes_ticks, render_function=render_function)
# St styling
wid.style(box_style='danger')
# Display widget
wid
The state can be updated as:
In [33]:
wid.set_widget_state({'x': list(range(5)), 'y': None}, allow_callback=True)
The AxesOptionsWidget
involves the AxesLimitsWidget
and AxesTicksWidget
widgets and also allows the selection of font-related options. As always, the initial options are provided in a dict
:
In [34]:
# Initial options
axes_options = {'render_axes': True,
'axes_font_name': 'serif',
'axes_font_size': 10,
'axes_font_style': 'normal',
'axes_font_weight': 'normal',
'axes_x_limits': None,
'axes_y_limits': None,
'axes_x_ticks': [0, 100],
'axes_y_ticks': None}
# Create widget
wid = AxesOptionsWidget(axes_options, render_function=render_function)
# Set styling
wid.style(box_style='warning', padding=6, border_visible=True, border_colour=map_styles_to_hex_colours('warning'))
# Display widget
wid
The state of the widget can be updated as:
In [35]:
axes_options = {'render_axes': True, 'axes_font_name': 'serif',
'axes_font_size': 10, 'axes_font_style': 'normal', 'axes_font_weight': 'normal',
'axes_x_limits': [0., 0.05], 'axes_y_limits': 0.1, 'axes_x_ticks': [0, 100], 'axes_y_ticks': None}
wid.set_widget_state(axes_options, allow_callback=True)
LegendOptionsWidget
allows to control the (many) options of renderinf the legend of a figure.
In [36]:
# Initial options
legend_options = {'render_legend': True,
'legend_title': '',
'legend_font_name': 'serif',
'legend_font_style': 'normal',
'legend_font_size': 10,
'legend_font_weight': 'normal',
'legend_marker_scale': 1.,
'legend_location': 2,
'legend_bbox_to_anchor': (1.05, 1.),
'legend_border_axes_pad': 1.,
'legend_n_columns': 1,
'legend_horizontal_spacing': 1.,
'legend_vertical_spacing': 1.,
'legend_border': True,
'legend_border_padding': 0.5,
'legend_shadow': False,
'legend_rounded_corners': True}
# Create widget
wid = LegendOptionsWidget(legend_options, render_function=render_function)
# Set styling
wid.style(border_visible=True, font_size=15)
# Display widget
wid
In [37]:
legend_options = {'render_legend': True, 'legend_title': 'asd', 'legend_font_name': 'sans-serif',
'legend_font_style': 'normal', 'legend_font_size': 60, 'legend_font_weight': 'normal',
'legend_marker_scale': 2., 'legend_location': 7, 'legend_bbox_to_anchor': (1.05, 1.),
'legend_border_axes_pad': 1., 'legend_n_columns': 2, 'legend_horizontal_spacing': 3.,
'legend_vertical_spacing': 7., 'legend_border': False,
'legend_border_padding': 0.5, 'legend_shadow': True, 'legend_rounded_corners': True}
wid.set_widget_state(legend_options, allow_callback=True)
The following simple widget controls the rendering of the grid lines of a plot, their style and width.
In [38]:
# Initial options
grid_options = {'render_grid': True,
'grid_line_width': 1,
'grid_line_style': '-'}
# Create widget
wid = GridOptionsWidget(grid_options, render_function=render_function)
# Set styling
wid.style(box_style='warning')
# Display widget
wid
In [39]:
wid.set_widget_state({'render_grid': True, 'grid_line_width': 10, 'grid_line_style': ':'})
The following widgets allow to select options regarding HOG, DSIFT, Daisy, LBP and IGO features.
In [40]:
# Initial options
hog_options = {'mode': 'dense',
'algorithm': 'dalaltriggs',
'num_bins': 9,
'cell_size': 8,
'block_size': 2,
'signed_gradient': True,
'l2_norm_clip': 0.2,
'window_height': 1,
'window_width': 1,
'window_unit': 'blocks',
'window_step_vertical': 1,
'window_step_horizontal': 1,
'window_step_unit': 'pixels',
'padding': True}
# Create widget
wid = HOGOptionsWidget(hog_options, render_function=render_function)
# Set styling
wid.style('info')
# Display widget
wid
In [41]:
# Initial options
dsift_options = {'window_step_horizontal': 1,
'window_step_vertical': 1,
'num_bins_horizontal': 2,
'num_bins_vertical': 2,
'num_or_bins': 9,
'cell_size_horizontal': 6,
'cell_size_vertical': 6,
'fast': True}
# Create widget
wid = DSIFTOptionsWidget(dsift_options, render_function=render_function)
# Set styling
wid.style('success')
# Display widget
wid
In [42]:
# Initial options
daisy_options = {'step': 1,
'radius': 15,
'rings': 2,
'histograms': 2,
'orientations': 8,
'normalization': 'l1',
'sigmas': None,
'ring_radii': None}
# Create widget
wid = DaisyOptionsWidget(daisy_options, render_function=render_function)
# Set styling
wid.style('danger')
# Display widget
wid
In [43]:
# Initial options
lbp_options = {'radius': list(range(1, 5)),
'samples': [8] * 4,
'mapping_type': 'u2',
'window_step_vertical': 1,
'window_step_horizontal': 1,
'window_step_unit': 'pixels',
'padding': True}
# Create widget
wid = LBPOptionsWidget(lbp_options, render_function=render_function)
# Set styling
wid.style(box_style='warning')
# Display widget
wid
In [44]:
wid = IGOOptionsWidget({'double_angles': True}, render_function=render_function)
wid